#include "EstadoDePreparacionDePartida.h"
#include "EstadoDeRealizacionDePartida.h"
#include <list>
#include <stdlib.h>
#include <time.h>
#include <map>
#include "Dado.h"
#include "TiradorDeDadosDesdeArchivo.h"
#include "ColocacionDeEjercitosInvalidaException.h"
#include "CartaDeObjetivos.h"
#include "Eliminacion.h"
#include <iostream>
#include <sstream>
using namespace std;

EstadoDePreparacionDePartida::EstadoDePreparacionDePartida(JuegoServerImp* juego) {
	this->juego = juego;
}
	
void EstadoDePreparacionDePartida::asignarEjercitos() {
	unsigned int ejercitosPorJugador;
	switch(juego->getCantidadDeJugadores()) {
		case 2 : ejercitosPorJugador = 11; break; //TODO: CAMBIAR ESTO POR 40.
		case 3 : ejercitosPorJugador = 11; break; //TODO:: CAMBIAR ESTO POR 35.
		case 4 : ejercitosPorJugador = 30; break;
		case 5 : ejercitosPorJugador = 25; break;
		case 6 : ejercitosPorJugador = 20; break;
	}
	
	for (int i = 0; i < juego->getCantidadDeJugadores(); i++)
		juego->getJugadores()->at(i)->asignarEjercitos(ejercitosPorJugador);
	sortearTurnos();
}

void EstadoDePreparacionDePartida::sortearTurnos() {
	int mayorValor = 0;
	bool hayUnMayor = false;
	//Aca es indistinto que llame a getDadosAtacante() o getDadosEnemigo. Solo necesito un dado.
	Dado* dado = this->juego->getDadosAtacante()->front();
	multimap<int,int> tiradas;
	for (int i = 0; i < juego->getCantidadDeJugadores(); i++) {
		juego->getTiradorDeDados()->tirar(dado);
		tiradas.insert(pair<int,int>(dado->getValor(),i));
		if (dado->getValor() > mayorValor)
			mayorValor = dado->getValor();
	}
	if (tiradas.count(mayorValor) == 1)
		hayUnMayor=true;
	while (!hayUnMayor) {
		pair<multimap<int,int>::iterator,multimap<int,int>::iterator> rango = tiradas.equal_range(mayorValor);
		multimap<int,int> desempate(rango.first,rango.second);
		tiradas.clear();
		mayorValor = 0;
		for (multimap<int,int>::iterator it = desempate.begin(); it != desempate.end(); ++it) {
			juego->getTiradorDeDados()->tirar(dado);
			tiradas.insert(pair<int,int>(dado->getValor(),it->second));
			if (dado->getValor() > mayorValor)
				mayorValor = dado->getValor();
		}
		if (tiradas.count(mayorValor) == 1)
			hayUnMayor=true;
	}
	vector<Jugador*>* jugadores = juego->getJugadores();
	Jugador* aux = jugadores->at(tiradas.find(mayorValor)->second);
	jugadores->erase(jugadores->begin()+tiradas.find(mayorValor)->second);
	jugadores->insert(jugadores->begin(),aux);
	juego->setTurno(0);
}

void EstadoDePreparacionDePartida::colocarEjercitos(Jugador* jugador, Territorio* territorio, unsigned int cantidadDeEjercitos) {
	if (jugador->getCantidadDeEjercitos() >= cantidadDeEjercitos) {
		//SI NO HAY EJERCITOS EN EL TERRITORIO
		if (territorio->getCantidadDeEjercitos() == 0) {
			jugador->quitarEjercitos(cantidadDeEjercitos);
			territorio->colocarEjercitos(cantidadDeEjercitos,jugador->getColor());
		}
		//SI HAY, ME FIJO QUE SEA DEL JUGADOR
		else if (territorio->getEnPoderDe() == jugador->getColor()) {
			//SI ES DEL JUGADOR, ME FIJO QUE TODOS LOS TERRITORIOS DEL MAPA TENGAN EJERCITOS
			if (juego->getMapa()->todosLosTerritoriosConEjercitos()) {
				territorio->colocarEjercitos(cantidadDeEjercitos,jugador->getColor());
				jugador->quitarEjercitos(cantidadDeEjercitos);
			}
			else
				throw ColocacionDeEjercitosInvalidaException("Todavia hay territorios vacios por ocupar");
		}
		else
			throw ColocacionDeEjercitosInvalidaException("El territorio esta en poder de "+territorio->getEnPoderDe());
	}
	bool todosLosEjercitosUbicados = true;
	for (vector<Jugador*>::iterator it = juego->getJugadores()->begin(); it != juego->getJugadores()->end(); ++it)
		if ((*it)->getCantidadDeEjercitos() != 0) {
			todosLosEjercitosUbicados = false;
			break;
		}
	if (todosLosEjercitosUbicados) {
		juego->setTurno(0);
		juego->setEstado(juego->getEstadoDeRealizacionDePartida());
		this->repartirCartasDeObjetivos();
		((EstadoDeRealizacionDePartida*)juego->getEstadoDeRealizacionDePartida())->reforzarEjercitos(juego->getJugadorEnTurno());
	}
	else
		juego->pasarAlSiguienteTurno();
		
}

void EstadoDePreparacionDePartida::repartirCartasDeObjetivos() {
	if (juego->getMapa()->getCantidadDeCartas() > 0) {
		map<int,int> asignaciones;
		vector<int> jugadores;
		for (int i = 0; i < juego->getCantidadDeJugadores(); i++)
			jugadores.push_back(i);
		int repartidas = 0;
		while (repartidas < juego->getCantidadDeJugadores()) {
			vector<int> cartas;
			for (int i = 0; i < juego->getMapa()->getCantidadDeCartas(); i++)
				cartas.push_back(i);
			//mezclo las cartas
			for (int i = 0; i < cartas.size(); i++) {
						int aux = cartas[i];
						srand(i);
						int pos = rand() % cartas.size();
						cartas[i] = cartas[pos];
						cartas[pos] = aux;
			}
			//asigno primero las de eliminacion (si es que hay)
			for (int i = 0; i < jugadores.size(); i++)
				for (int j = 0; j < cartas.size(); j++) {
					Jugador* aux = juego->getJugadores()->at(jugadores[i]);
					CartaDeObjetivos* carta = juego->getMapa()->obtenerCarta(cartas[j]);	
					if (dynamic_cast<Eliminacion*>(carta) != 0) {
						Eliminacion* eliminacion = dynamic_cast<Eliminacion*>(carta);
						//si es para eliminar a un jugador de un color inexistente, la elimino del maso
						if (juego->getJugador(eliminacion->getJugadorAEliminar()) == NULL)
							cartas.erase(cartas.begin()+j);
						//si no es para eliminar al color del jugador actual
						else if (eliminacion->getJugadorAEliminar() != aux->getColor()) {
							asignaciones[jugadores[i]] = cartas[j];
							repartidas++;
							cartas.erase(cartas.begin()+j);
							jugadores.erase(jugadores.begin()+i);
							break;
						}
						else if ((cartas.size() == 1) && (jugadores.size() == 1)) {
							repartidas++;
							cartas.erase(cartas.begin()+j);
							break;
						}
					}
				}
			//ahora asigno las de conquista (si es que hay)
			if (cartas.size() > 0)
				for (int i = 0; i < jugadores.size(); i++)
					for (int j = 0; j < cartas.size(); j++) {
						asignaciones[jugadores[i]] = cartas[j];
						repartidas++;
						break;
					}
		}
		//ahora recorro el map y efectivamente asigno una CartaDeObjetivos a cada Jugador
		for (map<int,int>::iterator it = asignaciones.begin(); it != asignaciones.end(); it++) {
			juego->getJugadores()->at(it->first)->setCartaDeObjetivos(juego->getMapa()->obtenerCarta(it->second));
			std::stringstream numeroDeCarta;
			numeroDeCarta<<it->second;
			juego->getServer()->enviarMensaje(juego->getJugadores()->at(it->first),"<cartaDeObjetivos numero=\""+numeroDeCarta.str()+"\"/>");
		}
	}
}

string EstadoDePreparacionDePartida::toString() {
	return "EstadoDePreparacionDePartida";
}

EstadoDePreparacionDePartida::~EstadoDePreparacionDePartida()
{
}
